/*
 * Copyright (c) 2006-2009 by Abacus Research AG, Switzerland.
 * All rights reserved.
 *
 * This file is part of the Abacus Formula Compiler (AFC).
 *
 * For commercial licensing, please contact sales(at)formulacompiler.com.
 *
 * AFC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * AFC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with AFC.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.formulacompiler.compiler.internal.model.interpreter;

import java.util.Date;

import org.formulacompiler.compiler.internal.AbstractLongType;
import org.formulacompiler.runtime.ComputationMode;
import org.formulacompiler.runtime.internal.Environment;
import org.formulacompiler.runtime.internal.RuntimeLong_v2;


abstract class InterpretedScaledLongType_Base extends InterpretedNumericType
{
	private final AbstractLongType num;
	private final RuntimeLong_v2.Context runtimeCx;


	public InterpretedScaledLongType_Base( AbstractLongType _type, ComputationMode _mode, Environment _env )
	{
		super( _type, _mode, _env );
		this.num = _type;
		this.runtimeCx = new RuntimeLong_v2.Context( _type.scale() );
	}


	protected final RuntimeLong_v2.Context getContext()
	{
		return this.runtimeCx;
	}

	private final int getScale()
	{
		return this.num.scale();
	}

	private final long zeroL()
	{
		return this.num.zero();
	}


	@Override
	public final Object adjustConstantValue( Object _value )
	{
		if (_value instanceof Number) {
			return this.num.valueOf( (Number) _value );
		}
		return _value;

	}


	@Override
	public Number toNumeric( Number _value )
	{
		return valueToScaledLongOrZero( _value );
	}


	private final long valueToScaledLongOrZero( Object _value )
	{
		if (null == _value) return zeroL();
		if (_value instanceof Long) return (Long) _value;

		// LATER This is kludgy and fragile. What if the parser/rewriter need to have longs too?
		/*
		 * The following two are generated by the expression parser and the rewriter. They must be
		 * treated as non-scaled values.
		 */
		if (_value instanceof Integer) return ((Integer) _value).longValue() * this.num.one();
		if (_value instanceof Double) return Math.round( ((Double) _value) * this.num.one() );
		if (_value instanceof Boolean) return ((Boolean) _value) ? this.num.one() : 0;
		if (_value instanceof String) {
			return RuntimeLong_v2.fromString( (String) _value, getContext(), getEnvironment(), getComputationMode() );
		}
		if (_value instanceof Date) {
			throw new IllegalArgumentException( "Cannot interpret java.util.Date - it is runtime time-zone specific." );
		}
		return zeroL();
	}


	@Override
	protected final int compareNumerically( Object _a, Object _b )
	{
		long a = valueToScaledLongOrZero( _a );
		long b = valueToScaledLongOrZero( _b );
		return (a == b) ? 0 : (a < b) ? -1 : 1;
	}


	@Override
	protected final int valueToInt( Object _value, int _ifNull )
	{
		if (_value instanceof Long) {
			long value = (Long) _value;
			return (int) (value / this.num.one());
		}
		return super.valueToInt( _value, _ifNull );
	}


	// Conversions for generated code:

	protected final boolean isScaled()
	{
		return getScale() != 0;
	}

	protected final long to_long( Object _o )
	{
		return valueToScaledLongOrZero( _o );
	}

	protected final long[] to_array( Object _value )
	{
		final Object[] consts = asArrayOfConsts( _value );
		final long[] r = new long[ consts.length ];
		int i = 0;
		for (Object cst : consts) {
			r[ i++ ] = to_long( cst );
		}
		return r;
	}


}